From 5b7ccf0b671e2999b62befc729a3e517a0433728 Mon Sep 17 00:00:00 2001 From: Bertrand Yuan Date: Mon, 15 Dec 2025 23:48:10 +0800 Subject: initial commit -- the front-end prototype The initial code is base on Anirudh's work. More to see at: https://github.com/techwithanirudh/shadcn-blog Therefore, the code in this commit is under MIT license. --- src/app/(home)/posts/[slug]/page.tsx | 145 +++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 src/app/(home)/posts/[slug]/page.tsx (limited to 'src/app/(home)/posts/[slug]/page.tsx') diff --git a/src/app/(home)/posts/[slug]/page.tsx b/src/app/(home)/posts/[slug]/page.tsx new file mode 100644 index 0000000..15a6bfd --- /dev/null +++ b/src/app/(home)/posts/[slug]/page.tsx @@ -0,0 +1,145 @@ +import { PostComments, Share } from '@/app/(home)/posts/[slug]/page.client'; +import { PostJsonLd } from '@/components/json-ld'; +import { Section } from '@/components/section'; +import { TagCard } from '@/components/tags/tag-card'; +import { createMetadata } from '@/lib/metadata'; +import { metadataImage } from '@/lib/metadata-image'; +import { type Page as MDXPage, getPost, getPosts } from '@/lib/source'; +import { cn } from '@/lib/utils'; +import { File, Files, Folder } from 'fumadocs-ui/components/files'; +import { InlineTOC } from 'fumadocs-ui/components/inline-toc'; +import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; +import defaultMdxComponents from 'fumadocs-ui/mdx'; +import type { Metadata } from 'next'; +import { notFound } from 'next/navigation'; +import Balancer from 'react-wrap-balancer'; +import { description as homeDescription } from 'src/app/layout.config'; + +function Header(props: { page: MDXPage; tags?: string[] }) { + const { page, tags } = props; + + return ( +
+
+
+

+ {page.data.title} +

+

+ {page.data.description} +

+
+
+ {tags?.map((tag) => ( + + ))} +
+
+
+ ); +} + +export default async function Page(props: { + params: Promise<{ slug: string }>; +}) { + const params = await props.params; + const page = getPost([params.slug]); + + if (!page) notFound(); + const { body: Mdx, toc, tags, lastModified } = page.data; + + const lastUpdate = lastModified ? new Date(lastModified) : undefined; + + return ( + <> +
+ +
+
+
+ +
+ +
+ +
+
+
+

Written by

+

{page.data.author}

+
+
+

+ Created At +

+

+ {new Date(page.data.date ?? page.file.name).toDateString()} +

+
+ {lastUpdate && ( +
+

+ Updated At +

+

{lastUpdate.toDateString()}

+
+ )} + +
+
+
+ + + ); +} + +export async function generateMetadata(props: { + params: Promise<{ slug: string }>; +}): Promise { + const params = await props.params; + const page = getPost([params.slug]); + + if (!page) notFound(); + + const title = page.data.title; + const description = page.data.description ?? homeDescription; + + return createMetadata( + metadataImage.withImage(page.slugs, { + title, + description, + openGraph: { + url: `/posts/${page.slugs.join('/')}`, + }, + alternates: { + canonical: page.url, + }, + }), + ); +} + +export function generateStaticParams(): { slug: string | undefined }[] { + return getPosts().map((page) => ({ + slug: page.slugs[0], + })); +} -- cgit v1.2.3